# -*- coding: utf-8 -*-
from django.forms import ValidationError, ChoiceField, CharField
from django.http import QueryDict, HttpResponse
from django.template.defaultfilters import slugify

from .. import utils, settings

import widgets

class Field(ChoiceField):
    """
    Base field that can be added to a *FilterForm*.
    Example usage::
    
        filters.Field(label="Category", allow_none="All",
            choices=Category.objects.values_list("slug", "name"),
            callback=filters.lookup_for("category__slug"))
            
    Field name is normally generated slugifying the passed label, but can be
    manually set passing *name="my-name"* to the field constructor.
    """
    def __init__(self, callback=None, allow_none=False, name=None,
        required=False, *args, **kwargs):
        super(Field, self).__init__(required=required, *args, **kwargs)
        self._callback = callback
        
        choices = [(unicode(k), v) for k, v in self.choices]
        if allow_none:
            choices.insert(0, (u"", allow_none)) 
        self.choices = choices
            
        self.name = slugify(self.label) if name is None else name
        
        self.is_selected = False
        self.value = None
        self.querydict = None
        
    def callback(self, queryset):
        """
        Fire the callback that manipulate the passed queryset.
        This is called from *FilterForm.run()* method.
        """
        if callable(self._callback):
            return self._callback(self, queryset)
        return self._callback
        
    def clean(self, value):
        """
        Wraps default django formfield *clean* method, raising error
        if the field is not selected (for redirect).
        """
        self.value = super(Field, self).clean(value)
        if self.value:
            self.is_selected = True            
            return self.value
        raise ValidationError(u"__empty__")
        
    def set_querydict(self, querydict):
        """
        Set the querydict in the field and in the associated widget.
        """
        # field and widget querydict must always be a *django.http.QueryDict*
        # instance
        if not querydict:
            querydict = QueryDict("")
        self.querydict = self.widget.querydict = querydict
    
    @property
    def value_display(self):
        """
        Return the humanized value of the selected choice.
        """
        return dict(self.choices).get(self.value)
        
    @property
    def title(self):
        """
        Return a string that can represent the selection.
        Can be used for automatic generation of page titles.
        """
        mapping = {'label': self.label, "value_display": self.value_display}
        return settings.TITLE_PATTERN % mapping
        
    @property
    def url_without_me(self):
        """
        Return the url (starting with *?*) that the page have without this field.
        """
        return utils.url_without_key(self.name, self.querydict)
        
                
class LinkField(Field):
    """
    Display choices as links (not as select boxes).
    By default choices are alphabetical letters.
    Example usage::
    
        filters.LinkField(label="Name starts with", allow_none="All", 
            callback=filters.lookup_for_letter("name"))
    """
    def __init__(self, widget=None, choices=(), base_url="", *args, **kwargs):
        if not choices:
            import string
            choices = [(unicode(i), unicode(i)) for i in string.lowercase]
            choices.append((u"#", u"#"))
        widget = widgets.Link(base_url) if widget is None else widget
        super(LinkField, self).__init__(widget=widget, choices=choices, 
            *args, **kwargs)
            
            
class SearchField(CharField):
    """
    Search field that can be added to a *FilterForm*.
    **Example usage**::
    
        search = filters.SearchField(label="Search for", 
            callback=searchers.in_fields("name", "description"))
            
    Field name is normally generated slugifying the passed label, but can be
    manually set passing *name="my-name"* to the field constructor.
    
    You can also pass *remove_label* keyword argument to change the default 
    *remove* label that is displayed near the search input for removing
    the search. You can override this globally adding *YAFINDER_REMOVE_LABEL*
    to your settings file.
    
    You can add **autocomplete** feature using::
        
        if search.autocomplete(request, callback):
            return search.response()
            
    and then loading *js/jquery.autocomplete.js*, *js/search_autocomplete.js*
    and *css/autocomplete.css*.
    Al js and css files are present in the directory *media* of this distribution.
    The jquery autocomplete library is taken from 
    http://www.pengoworks.com/workshop/jquery/autocomplete.htm (thanks a lot!).
            
    The *filters.from_queryset(queryset, field_name="name")* autocomplete 
    callback factory is provided as default example in the distribution.
    I takes the queryset of objects to look for and the name of the field
    is used for gettings objects that starts with the user query.
    
    
    Use *remove_label* to change the default "remove" label for search removing.
    You can also 
    
    Note that you can add the search field to a *FilterForm*. The search field
    exposes the same interface as *filters.Field*.
    """    
    def __init__(self, callback=None, name=None, required=False, widget=None,
        remove_label=settings.REMOVE_LABEL, *args, **kwargs):
        widget = widgets.Search if widget is None else widget
        super(SearchField, self).__init__(required=required, widget=widget, 
            *args, **kwargs)
        self.widget.remove_label = remove_label
        self._callback = callback
        
        self.name = slugify(self.label) if name is None else name
        
        self.is_selected = False
        self.value = None
        self.querydict = None
        self._autocomplete_content = ""
        
    def callback(self, queryset):
        """
        Fire the callback that manipulate the passed queryset.
        This is called from *FilterForm.run()* method.
        """
        if callable(self._callback):
            return self._callback(self, queryset)
        return self._callback
        
    def clean(self, value):
        """
        Wraps default django formfield *clean* method, raising error
        if the field is not selected (for redirect).
        """
        self.value = super(SearchField, self).clean(value)
        if self.value:
            self.is_selected = True            
            return self.value
        raise ValidationError(u"__empty__")
        
    def set_querydict(self, querydict):
        """
        Set the querydict in the field and in the associated widget.
        """
        # field and widget querydict must always be a *django.http.QueryDict*
        # instance
        if not querydict:
            querydict = QueryDict("")
        self.querydict = self.widget.querydict = querydict
    
    @property
    def value_display(self):
        """
        Return the query value.
        This is here only for mantaining the same interface as Field.
        """
        return self.value
        
    @property
    def title(self):
        """
        Return a string that can represent the query.
        Can be used for automatic generation of page titles.
        """
        mapping = {'label': self.label, "value_display": self.value_display}
        return settings.TITLE_PATTERN % mapping
        
    @property
    def url_without_me(self):
        """
        Return the url (starting with *?*) that the page have without this field.
        """
        return utils.url_without_key(self.name, self.querydict)
        
    def widget_attrs(self, widget):
        attrs = super(SearchField, self).widget_attrs(widget) or {}
        attrs["class"] = "yasearch"
        return attrs
        
    def autocomplete(self, request, callback, key=settings.AUTOCOMPLETE_KEY):
        """
        Return True if the request is an ajax request for autocompletion.
        """        
        if request.is_ajax():
            self_id = "id_%s" % self.name
            field_id = request.REQUEST.get("field_id")
            method = request.REQUEST.get("method")
            q = request.REQUEST.get(key, "").strip()
            if q and method == "autocomplete" and self_id == field_id:
                self._autocomplete_content = callback(q)
                return True
        return False
        
    def response(self):
        """
        Return the HttpResponse for autocompletion.
        """
        return HttpResponse(self._autocomplete_content)
